#pragma once

#include <QQuickItem>

class QmlGraphicsItem;
class QmlGraphicsView : public QQuickItem
{
    Q_OBJECT
public:
    explicit QmlGraphicsView(QQuickItem* parent = nullptr);
    virtual ~QmlGraphicsView() override;
    Q_DISABLE_COPY(QmlGraphicsView)

public:
    Q_PROPERTY(QQuickItem* scene READ getScene WRITE setScene NOTIFY sceneChanged FINAL)
    Q_PROPERTY(qreal zoomIncrement READ getZoomIncrement WRITE setZoomIncrement NOTIFY zoomIncrementChanged FINAL)
    Q_PROPERTY(qreal zoom READ getZoom WRITE setZoom NOTIFY zoomChanged FINAL)
    Q_PROPERTY(QQuickItem::TransformOrigin zoomOrigin READ getZoomOrigin WRITE setZoomOrigin NOTIFY zoomOriginChanged FINAL)
    Q_PROPERTY(qreal zoomMax READ getZoomMax WRITE setZoomMax NOTIFY zoomMaxChanged FINAL)
    Q_PROPERTY(qreal zoomMin READ getZoomMin WRITE setZoomMin NOTIFY zoomMinChanged FINAL)
    Q_PROPERTY(bool dragEnabled READ getDragEnabled WRITE setDragEnabled NOTIFY dragEnabledChanged FINAL)
    Q_PROPERTY(QQuickItem* rubberBandDragItem READ getRubberBandDragItem WRITE setRubberBandDragItem NOTIFY rubberBandDragItemChanged FINAL)

    Q_INVOKABLE void centerOn(QQuickItem* item);

    Q_INVOKABLE void centerOnPosition(QPointF position);

    Q_INVOKABLE void moveTo(QPointF position);

    Q_INVOKABLE void fitContentInView(qreal forceWidth = -1.0, qreal forceHeight = -1.0);

    Q_INVOKABLE QmlGraphicsItem* itemAt(const QPoint &pos) const;

    Q_INVOKABLE QList<QmlGraphicsItem*> itemsAt(const QPoint &pos) const;

    Q_INVOKABLE QList<QmlGraphicsItem*> items() const;

    Q_INVOKABLE QList<QmlGraphicsItem*> items(const QRectF &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;

    inline QQuickItem* getScene() noexcept
    {
        return m_sceneItem.data();
    }

    void setScene(QQuickItem* item);

    qreal getZoomIncrement() const
    {
        return m_zoomIncrement;
    }

    void setZoomIncrement(qreal zoomIncrement) noexcept
    {
        m_zoomIncrement = zoomIncrement;
        emit zoomIncrementChanged();
    }

    inline qreal getZoom() const noexcept
    {
        return m_zoom;
    }

    Q_INVOKABLE void setZoom(qreal zoom);

    Q_INVOKABLE void zoomOn(QPointF center, qreal zoom);

    bool isValidZoom(qreal zoom) const;

    QQuickItem::TransformOrigin getZoomOrigin() const noexcept
    {
        return m_zoomOrigin;
    }

    void setZoomOrigin(QQuickItem::TransformOrigin zoomOrigin);

    qreal getZoomMax() const noexcept
    {
        return m_zoomMax;
    }

    void setZoomMax(qreal zoomMax);

    qreal getZoomMin() const noexcept
    {
        return m_zoomMin;
    }

    void setZoomMin(qreal zoomMin);

    inline bool getDragEnabled() const noexcept
    {
        return m_dragActive;
    }

    void setDragEnabled(bool dragActive) noexcept;

    inline QQuickItem* getRubberBandDragItem()
    {
        return m_rubberBandDragItem;
    }

    void setRubberBandDragItem(QQuickItem* rubberBandDragItem);

signals:
    void zoomIncrementChanged();
    void zoomChanged();
    void zoomOriginChanged();
    void zoomMaxChanged();
    void zoomMinChanged();
    void clicked(QVariant pos);
    void rightClicked(QVariant pos);
    void sceneItemModified();
    void dragEnabledChanged();
    void sceneChanged();
    void rubberBandDragItemChanged();

private:
    void clickEvent(QMouseEvent* event);
    void resetData();
    void clearSelectedItems();

    QPointer<QQuickItem> m_sceneItem = nullptr;
    QPointer<QQuickItem> m_rubberBandDragItem = nullptr;
    qreal m_zoomIncrement = 0.05;
    qreal m_zoom = 1.0;
    QQuickItem::TransformOrigin m_zoomOrigin = QQuickItem::Center;
    qreal m_zoomMax = -1.0;
    qreal m_zoomMin = 0.04;
    bool m_dragActive = false;
    QSet<QmlGraphicsItem*> m_selectedItems;

protected:
    virtual void graphicsViewCtrlClicked(const QPointF &pos);

    virtual void graphicsViewClicked(const QPointF &pos);

    virtual void graphicsViewRightClicked(const QPointF &pos) {}

    virtual void sceneModified() {}

    virtual void rubberBandDragRectActivated(const QRectF &rect) {}

    virtual void rubberBandDragRectEnd(const QRectF &rect);

    virtual void mouseMoveEvent(QMouseEvent* event) override;

    virtual void mousePressEvent(QMouseEvent* event) override;

    virtual void mouseReleaseEvent(QMouseEvent* event) override;

    virtual void wheelEvent(QWheelEvent* event) override;

    bool m_leftButtonPressed = false;
    bool m_rightButtonPressed = false;
    QPointF m_lastPan{};
    QPointF m_mousePressPt{};
    QRectF m_sceneRect{};
};

QML_DECLARE_TYPE(QmlGraphicsView)
